import datetime
import pandas as pd
import Recommender.FeatureRepresentation as Feature
from Recommender.General import *

def Cache_Tags(realtime, articleId, tags):
    # article - tags

    # tags-articles
    # recalculate- needed
    pass


def Cache_Article_Tags_Batch(database, realtime, method):
    #
    print("Begin to Cache Item_Tags", datetime.datetime.now())

    # cache
    tagsByItem = {}

    # build params
    params = {}
    params["TagsByItem"] = tagsByItem
    params["Method"] = method

    #
    Batch_Loop(database, "Recommender", "TagsOnArticle",
               callback=Cache_Article_Tags_Batch_Callback,
               params=params,
               printField="title"
               )

    # 多层存储
    realtime.SetHashObjects("Article_Tags_" + method + "_Batch1", tagsByItem)

    #
    print("Finish to Cache Item_Tags", datetime.datetime.now())


def Cache_Article_Tags_Batch_Callback(count, documents, params):
    # ---Pass Variables---
    method = params["Method"]
    tagsByItem = params["TagsByItem"]

    #
    for tagDoc in documents:
        count += 1
        id = tagDoc["id"]
        tags = tagDoc[method]

        # ---Convert List to Dict---
        #tagsByItem[id] = WordWeightListToDict(tags)

        # ---Not Convert---
        tagsByItem[id] = tags

        # 单层存储
        # key = id + "_Tags_" + method
        # realtime.Set(key, tags)

    return count


# Tag1: item1 item2 item4
# Tag2: item2 item3
# Tag3: item2 item4
# Add to Realtime
def Cache_Tag_Articles_Batch(database, realtime, method):
    #
    print("Begin to Cache Tag_Items", datetime.datetime.now())

    # cache
    itemsByTag = {}

    # build params
    params = {}
    params["ItemsByTag"] = itemsByTag
    params["Method"] = method

    #
    Batch_Loop(database, "Recommender", "TagsOnArticle",
               callback=Cache_Tag_Items_Batch_Callback,
               params=params,
               printField="title"
               )

    # Sort by Article Weight
    for tag, contents in itemsByTag.items():
        sortedData = sorted(contents, key=lambda element: element["Weight"], reverse=True)
        # Reset
        itemsByTag[tag] = sortedData

    # 单层存储
    # for key, value in itemsByTag.items():
    #    realtime.Set(key + "_Items_" + method, value)

    # 多层存储
    realtime.SetHashObjects("Tag_Articles_" + method + "_Batch1", itemsByTag)

    #
    print("Finish to Cache Tag_Items", datetime.datetime.now())


def Cache_Tag_Items_Batch_Callback(count, documents, params):
    # ---Pass Variables---
    method = params["Method"]
    itemsByTag = params["ItemsByTag"]

    #
    for tagDoc in documents:
        count += 1
        id = tagDoc["id"]
        tags = tagDoc[method]
        for tag in tags:
            term = tag["Word"]

            #
            if term == "A股":
                a = 0

            weight = tag["Weight"]
            if term not in itemsByTag:
                itemsByTag[term] = []
            itemsByTag[term].append({"Article": id, "Weight": weight})

    return count


#
def Add_Tag_on_User_Batch(database, realtime):
    #
    print("Begin to Add Tags on Users", datetime.datetime.now())

    # cache
    user_items = {}  # user relation to every item

    # build params
    params = {}
    params["User_items"] = user_items
    params["Realtime"] = realtime

    #
    Batch_Loop(database, "Recommender", "UserAction_Plus",
               callback=Add_Tag_on_User_Batch_Callback,
               params=params,
               printField="action_time"
               )

    # ---Save to Realtime---
    realtime.SetHashObjects("User_Article" + "_Batch1", user_items)

    #
    print("Finish to Add Tags on Users", datetime.datetime.now())


#
def Add_Tag_on_User_Batch_Callback(count, documents, params):

    # ---Pass Variables---
    user_items = params["User_items"]
    realtime = params["Realtime"]

    #
    for action in documents:
        user = action["userpin"]
        article = action["article_id"]

        if action["type"] == "LIKE":
            # build user and item relation
            if user not in user_items:
                user_items[user] = {}
            user_items[user][article] = 1

            # add tags to user
            tags = realtime.GetHashDocument("Article_Tags_Corpus4_LDA_90_Batch1", article)
            if tags == None:
                print("No Article", article)
            else:  # add tags to user
                Add_Tags_on_User(user, tags, realtime)
            pass

    return count

#
def Add_Tags_on_User(user, tags, realtime):

    # user tags old
    userTags = realtime.GetHashDocument("User_Tags_Batch1", user)
    if userTags == None:
        # realtime.SetHash("User_Tags_Batch1", user, [])
        userTags = []

    # convert old tags list to Dict
    userTagsDict = {}
    for tag in userTags:
        userTagsDict[tag["Word"]] = tag["Weight"]

    # new tags list to Dict
    newTags = {}
    for tag in tags:
        newTags[tag["Word"]] = tag["Weight"]

    #
    for word, weight in newTags.items():
        if word not in userTagsDict:
            userTagsDict[word] = 0

        # ---所有标签无差别，等权重---
        userTagsDict[word] += 1

    # ---Convert back to List---
    updateUserTags = []
    for word, weight in userTagsDict.items():
        updateUserTags.append({"Word": word, "Weight": weight})

    # ---Sort by Article Weight---
    sortedData = sorted(updateUserTags, key=lambda element: element["Weight"], reverse=True)
    updateUserTags = sortedData

    # ---Set back to Redis---
    realtime.SetHashDocument("User_Tags_Batch1", user, updateUserTags)


#
def Recommander_SimpleTag_MaxTagedArticle(database, realtime, user, debug=False):

    #---Test what they read---
    if debug:
        print("User Read History", user)
        likeLog = realtime.GetHashDocument("User_Article_Batch1", user)
        if likeLog != None:
            for like, value in likeLog.items():
                articles = database.Find("Recommender","Article",{"id":like})
                if len(articles) == 0:
                    print(like, "Not Fount")
                    continue
                article = articles[0]
                print(like, article["title"])

    #
    maxUserTagsDepth = 100
    maxTagedArticleDepth = 100
    articlesById = {}
    articlesReasonById = {}
    userTags = realtime.GetHashDocument("User_Tags_Batch1", user)
    if userTags == None:
        print("User No Tag", user)
        return None

    i = 0
    for tag in userTags:
        i += 1
        if maxUserTagsDepth != 0 and i > maxUserTagsDepth:
            break
        #
        word = tag["Word"]
        articles = realtime.GetHashDocument("Tag_Articles_tfidf_Batch1", word)
        #
        if articles == None:
            continue
        #
        j = 0
        for article in articles:
            j += 1
            #
            # Touch max Depth
            if maxTagedArticleDepth != 0 and j > maxTagedArticleDepth:
                break

            articleId = article["Article"]

            # Add Null Element
            if articleId not in articlesById:
                articlesById[articleId] = 0
                articlesReasonById[articleId] = []

            #
            articlesById[articleId] += tag["Weight"] * article["Weight"]
            articlesReasonById[articleId].append([word, tag["Weight"], article["Weight"]])

    # End of Loop Tags

    data = []
    for id, score in articlesById.items():
        data.append([id, score])

    df = pd.DataFrame(data, columns=["ArticleId", "Score"])
    df.sort_values(by="Score", inplace=True, ascending=False)

    # print(df.head(10))

    if debug:
        count = 0
        for index, row in df.iterrows():
            count += 1
            id = row["ArticleId"]
            print(id, row["Score"], "Reason", articlesReasonById[id])
            if count > 10:
                break
    #
    return df


def Recommander_SimpleTag_TotalAitcle(database, realtime, user, articleIds=None):

    #
    if articleIds == None:
        articleIds = database.Find()

    #
    userTags = realtime.GetHashDocument("User_Tags_Batch1", user)
    #
    for articleId in articleIds:
        articleTags = realtime.GetHashDocument("Article_Tags_Batch1", articleId)
        DotProduct(userTags, articleTags)



def DotProduct(userTags,  articleTags):
    # Simple Dot Product Method
    score = 0
    for tag, tagWeight in articleTags.items():
        if tag in userTags.keys():
            score += tagWeight * userTags[tag]
    #
    return score


def Recommander_SimpleTag_Batch(database, realtime, method):
    # ---Load all (Active) Users---
    usersPins = database.Find()
    articleIds = database.Find()

    # ---Loop users---
    for user in usersPins:
        if method == "MaxTagedArticle":
            Recommander_SimpleTag_MaxTagedArticle(database, realtime, user, articleIds)
        elif method == "TotalAitcle":
            Recommander_SimpleTag_TotalAitcle(database, realtime, user)
        else:
            print("No method defined")


def Recommander_PersonalRank(user):
    pass
